package binchunk

import (
	"encoding/binary"
	"math"
)

type header struct {
	signature       [4]byte //文件标识
	version         byte    //版本号
	format          byte    //格式号
	luacData        [6]byte //luac数据
	cintSize        byte    //int大小
	sizetSize       byte    //size_t大小
	instructionSize byte    //指令大小
	luaIntegerSize  byte    //lua_Integer大小
	luaNumberSize   byte    //lua_Number大小
	luacInt         int64   //luac_int大小
	luacNum         float64 //luac_num大小
}

const (
	LUA_SIGNATURE    = "\x1bLua"
	LUAC_VERSION     = 0x53
	LUAC_FORMAT      = 0
	LUAC_DATA        = "\x19\x93\r\n\x1a\n"
	CINT_SIZE        = 4
	CSZIET_SIZE      = 8
	INSTRUCTION_SIZE = 4
	LUA_INTEGER_SIZE = 8
	LUA_NUMBER_SIZE  = 8
	LUAC_INT         = 0x5678
	LUAC_NUM         = 370.5
)

type Prototype struct {
	Source          string        //源文件名
	LineDefined     uint32        //起始行号
	LastLineDefined uint32        //结束行号
	NumParams       byte          //参数个数
	IsVararg        byte          //是否可变参数
	MaxStackSize    byte          //操作数栈的最大值
	Code            []uint32      //指令序列
	Constants       []interface{} //常量序列
	Upvalues        []Upvalue     //上值
	Protos          []*Prototype  //子函数原型
	LineInfo        []uint32      //行号信息
	LocVars         []LocVar      //局部变量信息
	UpvalueNames    []string      //上值名称
}

type binAryChunk struct {
	header                  //头部信息
	sizeUpvalues byte       //主函数upvalue数量
	mainFunc     *Prototype //主函数原型
}

type Upvalue struct {
	Instack byte //是否在栈中
	Idx     byte //索引
}

type LocVar struct {
	VarName string //变量名
	StartPC uint32 //起始指令
	EndPC   uint32 //结束指令
}

func Undump(data []byte) *Prototype {
	reader := &reader{data}
	reader.CheckHeader()
	reader.readByte()
	return reader.readProto("")
}

type reader struct {
	data []byte
}

func (self *reader) readByte() byte {
	b := self.data[0]
	self.data = self.data[1:]
	return b
}

func (self *reader) readUint32() uint32 {
	i := binary.LittleEndian.Uint32(self.data)
	self.data = self.data[4:]
	return i
}

func (self *reader) readUint64() uint64 {
	i := binary.LittleEndian.Uint64(self.data)
	self.data = self.data[8:]
	return i
}

func (self *reader) readLuaNumber() float64 {
	return math.Float64frombits(self.readUint64())
}
